// /////////////////////////////////////////////////////////////////////////////
//
// Name:            ScriptManager.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           The Script Manager is the central interface point to all
//                  script related functions of this program.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
// Framework files
#include "ScriptManager.h"

#ifdef __COMPILE_WITH_ANGELSCRIPT__

#include "../core/GameManager.h"

// AngelScript
#include "vendor/angelscript/scriptstdstring.h" // Standard Angelscript add-on.

#include "vendor/irrlicht/asIrrlicht.h"

#include "assets/asProcessors.h"
#include "components/asComponents.h"

#include "core/asAssetGroup.h"
#include "core/asAssetManager.h"
#include "core/asAssetProcessor.h"
#include "core/asDataStack.h"
#include "core/asDataStore.h"
#include "core/asEntity.h"
#include "core/asEntityComponent.h"
#include "core/asEntityManager.h"
#include "core/asEventManager.h"
#include "core/asGameManager.h"
#include "core/asGameState.h"
#include "core/asSerializable.h"
#include "core/asSerializedAttribute.h"
#include "core/asSerializedAttributes.h"

#include "scripting/asScript.h"
#include "scripting/asScriptManager.h"

#include "sound/asSoundManager.h"

void scriptGlobalEcho(const std::string& in);

// ScriptManager class
// Constructor of the ScriptManager class.
ScriptManager::ScriptManager()
{
}

// Deconstructor of the ScriptManager class.
ScriptManager::~ScriptManager()
{
}

// Initialises the Script Manager.
void ScriptManager::init()
{
    // Initialise the AngelScript engine.
    pEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

    RegisterStdString(pEngine);
    pEngine->SetMessageCallback(asFUNCTION(asMessageCallback), 0, asCALL_CDECL);

    // Register typedefs.
    pEngine->RegisterTypedef("c8",  "int8");
    pEngine->RegisterTypedef("f32", "float");
    pEngine->RegisterTypedef("f64", "double");
    pEngine->RegisterTypedef("s16", "int16");
    pEngine->RegisterTypedef("s32", "int");
    pEngine->RegisterTypedef("u16", "uint16");
    pEngine->RegisterTypedef("u32", "uint");
    pEngine->RegisterTypedef("u8",  "uint8");

    // Register interfaces.
    pEngine->RegisterInterface("IHasSlots");

    // Register Irrlicht.
    bindIrrlicht(pEngine);

    // Register classes and functions.
    bindAssetGroup(pEngine);
    bindAssetManager(pEngine);
    bindAssetProcessor(pEngine);

    bindDataStack(pEngine);
    bindDataStore(pEngine);

    bindSerializable(pEngine);
    bindSerializedAttribute(pEngine);
    bindSerializedAttributes(pEngine);

    bindEntity(pEngine);
    bindEntityComponent(pEngine);
    bindEntityManager(pEngine);

    bindEventManager(pEngine);

#ifdef __COMPILE_WITH_SFML_AUDIO__
    bindSoundManager(pEngine);
#endif // __COMPILE_WITH_SFML_AUDIO__

    bindScript(pEngine);
    bindScriptManager(pEngine);

    bindAssetProcessors(pEngine);
    bindComponents(pEngine);

    bindGameState(pEngine);
    bindGameManager(pEngine);

	bindGlobals();
}

// Clears the Script Manager.
void ScriptManager::clear()
{
    // Remove scripts.
    removeScripts();

    // Remove script sections.
    mScriptSections.clear();

    // Deinitialise the AngelScript engine.
    pEngine->Release();
}

// Angelscript: Increases the reference counter.
void ScriptManager::grab()
{
    // Dummy function.
}

// Angelscript: Decreases the reference counter.
void ScriptManager::drop()
{
    // Dummy function.
}

// Gets the AngelScript engine.
asIScriptEngine* ScriptManager::getEngine() const
{
    return pEngine;
}

// Adds a script to the Script Manager.
bool ScriptManager::addScript(Script *script)
{
    // Did we get a valid pointer?
    if(script == NULL)
      return false;

    // Look if a script with given name doesn't already exist.
    if(getScript(script->getName()))
      return false;

    // Add the script.
    mScripts.push_back(script);
    script->grab();

    return true;
}

// Adds a script section to the Script Manager.
bool ScriptManager::addScriptSection(const std::string &name, const std::string &section)
{
    // Does the script section already exist?
    if(mScriptSections.find(name) != mScriptSections.end())
      return false;

    // Add the new script section.
    mScriptSections[name] = section;

    return true;
}

// Adds the standard script sections to the given script.
void ScriptManager::addScriptSectionsToScript(Script *script)
{
    // Did we get a valid pointer?
    if(script == NULL)
      return;

    // Add the sections.
    asIScriptModule *module = script->getModule();
    std::map<std::string, std::string>::iterator it;

    for(it = mScriptSections.begin(); it != mScriptSections.end(); it++)
      module->AddScriptSection((it->first).c_str(), (it->second).c_str());
}

// Creates (and adds) an script with the given name.
Script* ScriptManager::createScript(const std::string &name, bool grab)
{
    // Look if a script with given name doesn't already exist.
    if(grab && getScript(name))
      return NULL;

    // Add the script.
    Script *script = new Script(name);

    if(grab)
    {
       mScripts.push_back(script);
       script->grab();
    }

    return script;
}

// Creates an script with the given name, loaded from the given file.
Script* ScriptManager::createScriptFromFile(const std::string &fileName, bool grab)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Retrieve base name.
    core::stringw wFilename = core::stringw(fileName.c_str());
    std::string fileBase = (fileSystem->getFileBasename(wFilename)).c_str();

    // Look if a script with given name doesn't already exist.
    if(grab && getScript(fileBase))
      return NULL;

    // Add the script.
    Script *script = new Script(fileBase);

	bool success = script->loadScript(fileName);

	if(!success)
	{
		printf("-- : Error : cannot find file : %s\n", fileName.c_str());
		return NULL;
	}

    if(grab)
    {
       mScripts.push_back(script);
       script->grab();
    }

    return script;
}

// Creates an script with the given name, loaded from the given file.
Script* ScriptManager::createScriptFromFile(IReadFile *file, bool grab)
{
    // Look if a script with given name doesn't already exist.
    if(grab && getScript( (file->getFileName()).c_str() ))
      return NULL;

    // Add the script.
    Script *script = new Script( (file->getFileName()).c_str() );
    script->loadScript(file);

    if(grab)
    {
       mScripts.push_back(script);
       script->grab();
    }

    return script;
}

// Gets the script with the given ID.
Script* ScriptManager::getScript(const u32 id)
{
    for(u32 i = 0; i < mScripts.size(); i++)
    {
       if(mScripts[i]->getID() == id)
         return mScripts[i];
    }

    return NULL;
}

// Gets the script with the given name.
Script* ScriptManager::getScript(const std::string &name)
{
    for(u32 i = 0; i < mScripts.size(); i++)
    {
       if(mScripts[i]->getName() == name)
         return mScripts[i];
    }

    return NULL;
}

// Removes all scripts.
void ScriptManager::removeScripts()
{
    for(u32 i = 0; i < mScripts.size(); i++)
      mScripts[i]->drop();

    mScripts.clear();
}

// Removes all script sections,
void ScriptManager::removeScriptSections()
{
    mScriptSections.clear();
}

// Removes the given script.
bool ScriptManager::removeScript(Script *script)
{
    // Did we get a valid pointer?
    if(script == NULL)
      return false;

    // Try to remove the script.
    vector<Script*>::iterator it;

    for(it = mScripts.begin(); it < mScripts.end(); it++)
    {
        Script *scr = *it;

        if(scr == script)
        {
           scr->drop();
           mScripts.erase(it);
           return true;
        }
    }

    // We couldn't find the script and thus couldn't remove it.
    return false;
}

// Removes the script with the given ID.
bool ScriptManager::removeScript(const u32 id)
{
    // Try to remove the script.
    vector<Script*>::iterator it;

    for(it = mScripts.begin(); it < mScripts.end(); it++)
    {
        Script *scr = *it;

        if(scr->getID() == id)
        {
           scr->drop();
           mScripts.erase(it);
           return true;
        }
    }

    // We couldn't find the script and thus couldn't remove it.
    return false;
}

// Removes the script with the given name.
bool ScriptManager::removeScript(const std::string &name)
{
    // Try to remove the script.
    vector<Script*>::iterator it;

    for(it = mScripts.begin(); it < mScripts.end(); it++)
    {
        Script *scr = *it;

        if(scr->getName() == name)
        {
           scr->drop();
           mScripts.erase(it);
           return true;
        }
    }

    // We couldn't find the script and thus couldn't remove it.
    return false;
}

// Removes the script section with the given name.
bool ScriptManager::removeScriptSection(const std::string &name)
{
    // Try to remove the script.
    std::map<std::string, std::string>::iterator it;
    it = mScriptSections.find(name);

    if(it != mScriptSections.end())
    {
       mScriptSections.erase(it);
       return true;
    }

    // We couldn't find the script section and thus couldn't remove it.
    return false;
}

//! Register all script side globals and helpers
void ScriptManager::bindGlobals()
{
	int r;

	//Register debug functions
	r = pEngine->RegisterGlobalFunction("void echo(string &in)",
				asFUNCTION(asEcho), asCALL_CDECL); assert(r >= 0);

}


// AngelScript functions
// Implement a simple message callback function
void asMessageCallback(const asSMessageInfo *msg, void *param)
{
  const char *type = "ERR ";
  if(msg->type == asMSGTYPE_WARNING)
    type = "WARN";
  else if(msg->type == asMSGTYPE_INFORMATION)
    type = "INFO";
  printf("%s (Line %d, Column %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

// Implements a way to output a message to stdout.
void asEcho(const std::string& in)
{
	printf("%s", in.c_str());
	fflush(stdout);
}

#endif // __COMPILE_WITH_ANGELSCRIPT__

// End of File
